package com.tmt.api.web;

import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

import com.swak.Constants;
import com.swak.annotation.FluxReferer;
import com.swak.annotation.GetMapping;
import com.swak.annotation.PostMapping;
import com.swak.annotation.RestApi;
import com.swak.codec.Encodes;
import com.swak.entity.Result;
import com.swak.exception.ErrorCode;
import com.swak.security.Principal;
import com.swak.security.Subject;
import com.swak.security.Token;
import com.swak.utils.StringUtils;
import com.swak.wechat.user.SessionToken;
import com.swak.wechat.user.SnsToken;
import com.swak.wechat.user.UserInfo;
import com.tmt.api.entity.User;
import com.tmt.api.entity.UserAccount;
import com.tmt.api.service.UserServiceAsync;
import com.tmt.cache.CacheService;
import com.tmt.wechat.WechatService;

import io.vertx.ext.web.RoutingContext;

/**
 * 系统登陆
 * 
 * @author lifeng
 * @date 2020年4月14日 下午7:39:37
 */
@RestApi(path = "/api/login", value = "loginApi")
public class LoginApi {

	/**
	 * 异步的用户服务
	 */
	@FluxReferer
	private UserServiceAsync userService;

	/**
	 * 微信服务
	 */
	@FluxReferer
	private WechatService wechatService;

	/**
	 * 缓存服务
	 */
	@FluxReferer
	private CacheService cacheService;

	// *************** 用户名和密码登陆 ******************
	/**
	 * 通过： UserName 和 Password 来登陆系统 <br>
	 * 
	 * @param userName 用户名
	 * @param password 密码
	 * @param subject  主体
	 * @param context  请求上下文
	 * @return
	 */
	@PostMapping("/account")
	public CompletionStage<Result> loginAccount(String userName, String password, Subject subject,
			RoutingContext context) {
		return userService.getByAccount(userName).thenApply(user -> {
			if (user != null && user.getAccount() != null && user.getAccount().getPassword().equals(password)) {
				return user;
			}
			return null;
		}).thenCompose(user -> {
			if (user != null) {
				Principal principal = new Principal();
				principal.setId(user.getId());
				subject.setPrincipal(principal);
			}
			return subject.setAttach("1", "2").login(context);
		}).thenApply((token) -> {
			return token != null ? Result.success(token) : Result.error("token生成失败！");
		});
	}

	// *************** 小程序登陆 ******************

	/**
	 * 小程序的登陆
	 * 
	 * @param userName 用户名
	 * @param password 密码
	 * @return
	 */
	@PostMapping("/xcx")
	public CompletionStage<Result> xcx(String code, UserInfo user, Subject subject, RoutingContext context) {
		return wechatService.oauth2Session(code).thenCompose(token -> {
			if (token != null && StringUtils.isNotBlank(token.getSession_key())) {
				return this.user2Token(token, user, context, subject);
			}
			return CompletableFuture.completedFuture(null);
		}).thenApply((token) -> {
			return token != null ? Result.success(token) : Result.error("token生成失败！");
		});
	}

	/**
	 * 为用户生成token信息 -- 登陆
	 * 
	 * @return
	 */
	private CompletableFuture<Token> user2Token(SessionToken token, UserInfo userInfo, RoutingContext context,
			Subject subject) {
		return this.userService.getByAccount(token.getOpenid()).thenCompose(user -> {
			if (user != null) {
				return CompletableFuture.completedFuture(user);
			}
			return this.userInfo2User(userInfo);
		}).thenCompose(user -> {
			Principal principal = new Principal();
			principal.setId(user.getId());
			subject.setPrincipal(principal);
			return subject.login(context);
		});
	}

	/**
	 * 为用户生成token信息 -- 登陆 -> 注册
	 * 
	 * @param userInfo
	 * @return User
	 */
	private CompletableFuture<User> userInfo2User(UserInfo userInfo) {
		UserAccount account = new UserAccount();
		account.setId(userInfo.getOpenid());
		account.setName(userInfo.getNickname());
		account.setHeadimg(userInfo.getHeadimgurl());
		return this.userService.register(account);
	}

	// *************** 微信网页登陆，需要配合页面面完成 ******************

	/**
	 * 1. 发起微信登陆
	 * 
	 * @return
	 */
	@GetMapping("/wechat")
	public CompletionStage<String> wechat(String realm) {
		String state = StringUtils.defaultIfBlank(realm, "admin"); // 不需要绑定，但可以传递不同的参数
		return toAuthorize(new StringBuilder("http://admin.xxx.cn").append("/#/login").toString(), state);
	}

	/**
	 * 授权的地址
	 * 
	 * @param url
	 * @return
	 */
	private CompletableFuture<String> toAuthorize(String url, String state) {

		// 参数不正确
		if (StringUtils.isBlank(url) || StringUtils.isBlank(state)) {
			return CompletableFuture.completedFuture(ErrorCode.ACCESS_DENIED.toJson());
		}

		// 服务的地址
		String redirectUri = new StringBuilder("http://api.xxx.cn").append("/api/login/wechat/rt").append("?to=")
				.append(Encodes.urlEncode(url)).toString();

		// 授权到公众号
		return wechatService.toAuthorize(redirectUri, state);
	}

	/**
	 * 2. 微信授权回调
	 * 
	 * @return
	 */
	@GetMapping("/wechat/rt")
	public CompletionStage<String> rt(String code, String state, String to, Subject subject, RoutingContext context) {
		return wechatService.oauth2AccessToken(code, state).thenCompose(res -> {
			if (res != null && StringUtils.isNotBlank(res.getAccess_token())) {
				return this.user2Token(res, state, subject, context);
			}
			return CompletableFuture.completedFuture(StringUtils.EMPTY);
		}).thenCompose(token -> {
			String _token = UUID.randomUUID().toString();
			if (StringUtils.isNotBlank(token)) {
				return cacheService.addToken(_token, token);
			}
			return CompletableFuture.completedFuture(_token);
		}).thenApply(token -> {
			StringBuilder _to = new StringBuilder(to);
			if (StringUtils.contains(to, "?")) {
				_to.append("&token=").append(token);
			} else {
				_to.append("?token=").append(token);
			}
			return new StringBuffer(Constants.REDIRECT_URL_PREFIX).append(_to.toString()).toString();
		});
	}

	/**
	 * 为用户生成token信息 -- 登陆
	 * 
	 * @return
	 */
	private CompletableFuture<String> user2Token(SnsToken token, String realm, Subject subject,
			RoutingContext context) {
		return this.userService.getByAccount(token.getOpenid()).thenCompose(user -> {
			if (user != null) {
				return CompletableFuture.completedFuture(user);
			}
			return this.registerByAccount(token, realm);
		}).thenCompose(user -> {
			return this.userLogin(user, subject, context);
		});
	}

	/**
	 * 为用户生成token信息 -- 登陆 -> 注册
	 * 
	 * @param token
	 * @param realm
	 * @return
	 */
	private CompletableFuture<User> registerByAccount(SnsToken token, String realm) {
		return this.wechatService.accessToken2Userinfo(token).thenCompose(res -> {
			if (res != null) {
				return this.registerByAccount(res, realm);
			}
			return CompletableFuture.completedFuture(null);
		});
	}

	/**
	 * 为用户生成token信息
	 * 
	 * @return
	 */
	private CompletableFuture<User> registerByAccount(UserInfo userInfo, String realm) {
		UserAccount account = new UserAccount();
		account.setId(userInfo.getOpenid());
		account.setName(userInfo.getNickname());
		account.setHeadimg(userInfo.getHeadimgurl());
		return this.userService.register(account);
	}

	/**
	 * 生成 token 信息
	 * 
	 * @param user
	 * @param subject
	 * @return
	 */
	private CompletionStage<String> userLogin(User user, Subject subject, RoutingContext context) {
		return subject.login(context).thenApply(token -> token.getToken());
	}

	/**
	 * 3. 授权登录(可以在这里加一个模拟登录)
	 * 
	 * @return
	 */
	@PostMapping("oauth")
	public CompletionStage<Result> oauth(RoutingContext context, String token) {
		return cacheService.getToken(token).thenApply(res -> Result.success(res));
	}

	// *************** PC 管理端扫码登陆过于复杂，需要pc端，手机端配合才能完成登陆 ******************
}
